﻿using System;
using System.Collections.Generic;
using Cpuss.Properties;

namespace Cpuss
{
    /// <summary>
    /// Runs a particular simulation n times and aggregates the data.
    /// </summary>
    public class RepeatRunner
    {
        private readonly List<double> _cpuUtilizations;
        private readonly Guid _id;
        private readonly int _large;
        private readonly int _medium;
        private readonly int _processCount;
        private readonly int _repeat;
        private readonly List<double> _responseTimeStandardDeviations;
        private readonly int _small;
        private readonly IStrategy _strategy;
        private readonly List<double> _turnaroundTimeStandardDeviations;
        private readonly List<double> _waitTimeStandardDeviations;
        private readonly Pairs<int, double> _throughputs;
        private readonly int _timeframe;

        /// <summary>
        /// Initializes a new instance of the <see cref="RepeatRunner"/> class.
        /// </summary>
        /// <param name="small">Number of small processes.</param>
        /// <param name="medium">Number of medium processes.</param>
        /// <param name="large">Numer of large processes.</param>
        /// <param name="strategy">Strategy to use.</param>
        /// <param name="repeat">Number of times to run simulation.</param>
        /// <param name="timeframe">Timeframe for throughput.</param>
        /// <exception cref="ArgumentOutOfRangeException">repeat</exception> is less than <strong>1</strong>.
        /// <exception cref="ArgumentException"><strong>No</strong> processes have been selected to run.</exception>
        public RepeatRunner(int small, int medium, int large, IStrategy strategy, int repeat, int timeframe)
        {
            if (repeat < 2)
            {
                throw new ArgumentOutOfRangeException("repeat", Resources.RunsLessThanTwo);
            }
            else if ((small + medium + large) < 1)
            {
                throw new ArgumentException(Resources.NoProcessesToRun);
            }
            else if (small < 0 || medium < 0 || large < 0)
            {
                throw new ArgumentOutOfRangeException(Resources.NumberOfProcessesNegative);
            }

            _id = Guid.NewGuid();
            _small = small;
            _medium = medium;
            _large = large;
            _strategy = strategy;
            _repeat = repeat;
            _processCount = small + medium + large;
            _waitTimeStandardDeviations = new List<double>();
            _cpuUtilizations = new List<double>();
            _turnaroundTimeStandardDeviations = new List<double>();
            _responseTimeStandardDeviations = new List<double>();
            _throughputs = new Pairs<int, double>();
            _timeframe = timeframe;
        }

        /// <summary>
        /// Gets the Id of the <see cref="RepeatRunner"/>.
        /// </summary>
        public Guid Id
        {
            get { return _id; }
        }

        /// <summary>
        /// Gets the standard deviations collected from the repeated simulations.
        /// </summary>
        public List<double> WaitTimeStandardDeviations
        {
            get { return _waitTimeStandardDeviations; }
        }

        /// <summary>
        /// Gets the number of repeat the simulation has been ran for.
        /// </summary>
        public int Repeat
        {
            get { return _repeat; }
        }

        /// <summary>
        /// Gets the name of the <see cref="IStrategy"/> being used.
        /// </summary>
        public string Strategy
        {
            get { return _strategy.ToString(); }
        }

        /// <summary>
        /// Gets the total number of processes in the <see cref="RepeatRunner"/> simulation.
        /// </summary>
        public int ProcessCount
        {
            get { return _processCount; }
        }

        /// <summary>
        /// Gets the number of small sized processes being used in the <see cref="RepeatRunner"/>.
        /// </summary>
        public int Small
        {
            get { return _small; }
        }

        /// <summary>
        /// Gets the number of medium sized processes being used in the <see cref="RepeatRunner"/>.
        /// </summary>
        public int Medium
        {
            get { return _medium; }
        }

        /// <summary>
        /// Gets the number of large sized processes being used in the <see cref="RepeatRunner"/>.
        /// </summary>
        public int Large
        {
            get { return _large; }
        }


        ///<summary>
        /// Gets the CPU utilization %'s from the repeated simulations.
        ///</summary>
        public List<double> CpuUtilizations
        {
            get { return _cpuUtilizations; }
        }

        /// <summary>
        /// Gets the Turnaround times from the repeated simulations.
        /// </summary>
        public List<double> TurnaroundTimeStandardDeviations
        {
            get { return _turnaroundTimeStandardDeviations; }
        }

        /// <summary>
        /// Gets the throughput times (window * throughput mean).
        /// </summary>
        public Pairs<int, double> Throughputs
        {
            get { return _throughputs; }
        }

        /// <summary>
        /// Gets the response time standard deviations from the repeated simulation.
        /// </summary>
        public List<double> ResponseTimeStandardDeviations
        {
            get { return _responseTimeStandardDeviations; }
        }

        /// <summary>
        /// Gets the window timeframe being used for throughput.
        /// </summary>
        public int WindowTimeframe
        {
            get { return _timeframe; }
        }
            /// <summary>
        /// Runs the <see cref="RepeatRunner"/>.
        /// </summary>
        public void Run()
        {
            for (int i = 0; i < _repeat; i++)
            {
                var runner = new Runner(_small, _medium, _large, _strategy, _timeframe);
                runner.Run();
                _waitTimeStandardDeviations.Add(runner.GetWaitTimeStandardDeviation());
                _cpuUtilizations.Add(runner.CpuUtilization);
                _turnaroundTimeStandardDeviations.Add(runner.GetTurnaroundTimeStandardDeviation());
                _responseTimeStandardDeviations.Add(runner.GetResponseTimeStandardDeviation());
                _throughputs.Add(new Pair<int, double>()
                                     {
                                         First = runner.WindowTimeframe,
                                         Second = runner.GetThroughputMean()
                                     });
            }
        }

        /// <summary>
        /// Gets the mean throughput for the simulations that have been ran.
        /// </summary>
        /// <returns>Mean window time and process throughput.</returns>
        public Pair<double, double> GetThroughputStats()
        {
            double windowMean = 0;
            double processThroughput = 0;
            foreach (Pair<int, double> pair in _throughputs)
            {
                processThroughput += pair.Second;
                windowMean += pair.First;
            }
            return new Pair<double, double>()
                       {
                           First = (windowMean/_throughputs.Count),
                           Second = (processThroughput/_throughputs.Count)
                       };
        }
    }
}